
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include "paho-mqtt-c/src/MQTTClient.h"

#define DEFAULT_BROKER "tcp://mqtt.xxx.cn:1883"
#define DEFAULT_TOPIC "device_control"
#define QOS 2
#define TIMEOUT 10000L

char* broker = NULL;
char* username = NULL;
char* password = NULL;
char* subscribe_topic = NULL;
char* publish_topic = NULL;
char* clientid = NULL;

volatile MQTTClient_deliveryToken deliveredtoken;
MQTTClient_connectOptions conn_opts;
int reconnect_attempts = 0;
#define MAX_RECONNECT_ATTEMPTS 5
#define RECONNECT_DELAY 5000

void delivered(void *context, MQTTClient_deliveryToken dt) {
    printf("Message with token value %d delivery confirmed\n", dt);
    deliveredtoken = dt;
}

#include <string.h>

int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message) {
    printf("Message arrived\n");
    printf("     topic: %s\n", topicName);
    printf("   message: %.*s\n", message->payloadlen, (char*)message->payload);

    // Simple JSON parsing
    char *payload = (char*)message->payload;
    char *method_start = strstr(payload, "\"method\":\"echo\"");
    if(method_start && publish_topic) {
        char *data_start = strstr(payload, "\"data\":\"");
        if(data_start) {
            data_start += 8; // Skip "\"data\":\""
            char *data_end = strchr(data_start, '\"');
            if(data_end) {
                // Build response
                char response[1024];
                int data_len = data_end - data_start;
                snprintf(response, sizeof(response), 
                    "{\"service\":\"echo\",\"data\":\"%.*s\"}", 
                    data_len, data_start);
                
                // Publish response
                MQTTClient_message pubmsg = MQTTClient_message_initializer;
                pubmsg.payload = response;
                pubmsg.payloadlen = strlen(response);
                pubmsg.qos = QOS;
                pubmsg.retained = 0;
                
                MQTTClient_deliveryToken token;
                MQTTClient_publishMessage(*(MQTTClient*)context, publish_topic, &pubmsg, &token);
            }
        }
    }

    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);
    return 1;
}

void connlost(void *context, char *cause) {
    printf("\nConnection lost\n");
    printf("     cause: %s\n", cause);
    
    if (!context) {
        printf("No client context available for reconnection\n");
        return;
    }
    
    MQTTClient client = *(MQTTClient*)context;
    int rc;
    
    while (reconnect_attempts < MAX_RECONNECT_ATTEMPTS) {
        printf("Attempting to reconnect (%d/%d)...\n", 
               reconnect_attempts + 1, MAX_RECONNECT_ATTEMPTS);
        
        rc = MQTTClient_connect(client, &conn_opts);
        if (rc == MQTTCLIENT_SUCCESS) {
            printf("Reconnected successfully\n");
            reconnect_attempts = 0;
            
            // Re-subscribe if needed
            if (subscribe_topic) {
                if ((rc = MQTTClient_subscribe(client, subscribe_topic, QOS)) != MQTTCLIENT_SUCCESS) {
                    printf("Failed to re-subscribe, return code %d\n", rc);
                } else {
                    printf("Re-subscribed to topic %s\n", subscribe_topic);
                }
            }
            return;
        }
        
        printf("Reconnect failed, retrying in %d seconds...\n", RECONNECT_DELAY/1000);
        reconnect_attempts++;
        
        #if defined(_WIN32)
            Sleep(RECONNECT_DELAY);
        #else
            usleep(RECONNECT_DELAY * 1000);
        #endif
    }
    
    printf("Max reconnect attempts reached. Giving up.\n");
    exit(EXIT_FAILURE);
}

void print_usage() {
    printf("Usage: mqtt_example [-b broker] [-u username] [-p password] [-s subscribe_topic] [-P publish_topic] [-c clientid]\n");
    printf("Defaults:\n");
    printf("  broker: %s\n", DEFAULT_BROKER);
    printf("  subscribe_topic: %s\n", DEFAULT_TOPIC);
    printf("  clientid: same as username\n");
}

int main(int argc, char* argv[]) {
    int opt;
    while ((opt = getopt(argc, argv, "b:u:p:s:c:P:")) != -1) {
        switch (opt) {
            case 'b': broker = optarg; break;
            case 'u': username = optarg; break;
            case 'p': password = optarg; break;
            case 's': subscribe_topic = optarg; break;
            case 'P': publish_topic = optarg; break;
            case 'c': clientid = optarg; break;
            default: print_usage(); exit(EXIT_FAILURE);
        }
    }

    if (!broker) broker = DEFAULT_BROKER;
    if (!subscribe_topic) subscribe_topic = DEFAULT_TOPIC;
    if (!clientid) clientid = username;

    if (!username || !password) {
        printf("Username and password are required\n");
        print_usage();
        exit(EXIT_FAILURE);
    }

    MQTTClient client;
    memset(&conn_opts, 0, sizeof(conn_opts));
    conn_opts.struct_id[0] = 'M';
    conn_opts.struct_id[1] = 'Q';
    conn_opts.struct_id[2] = 'T';
    conn_opts.struct_id[3] = 'C';
    conn_opts.struct_version = 8;
    conn_opts.keepAliveInterval = 60;
    conn_opts.cleansession = 1;
    conn_opts.reliable = 1;
    conn_opts.connectTimeout = 30;
    int rc;
    
    printf("Connecting to broker: %s\n", broker);
    
    MQTTClient_create(&client, broker, clientid,
        MQTTCLIENT_PERSISTENCE_NONE, NULL);
    
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    conn_opts.connectTimeout = 10;
    conn_opts.username = username;
    conn_opts.password = password;
    
    if ((rc = MQTTClient_setCallbacks(client, &client, connlost, msgarrvd, delivered)) != MQTTCLIENT_SUCCESS) {
        printf("Failed to set callbacks, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
    
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
        printf("Failed to connect, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
    
    printf("Connected\n");
    printf("Subscribing to topic %s\n", subscribe_topic);
    
    if ((rc = MQTTClient_subscribe(client, subscribe_topic, QOS)) != MQTTCLIENT_SUCCESS) {
        printf("Failed to subscribe, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
    
    printf("Subscribed\n");
    
    if (publish_topic) {
        printf("Ready to publish messages to topic: %s\n", publish_topic);
        printf("Type your message and press Enter to publish (empty to quit):\n");
        
        char message[1024];
        while (1) {
            printf("> ");
            if (fgets(message, sizeof(message), stdin)) {
                // Remove newline
                message[strcspn(message, "\n")] = 0;
                
                if (strlen(message) == 0) {
                    printf("Empty message - not publishing\n");
                    continue;
                }
                
                MQTTClient_message pubmsg = MQTTClient_message_initializer;
                pubmsg.payload = message;
                pubmsg.payloadlen = strlen(message);
                pubmsg.qos = QOS;
                pubmsg.retained = 0;
                
                MQTTClient_deliveryToken token;
                if ((rc = MQTTClient_publishMessage(client, publish_topic, &pubmsg, &token)) != MQTTCLIENT_SUCCESS) {
                    printf("Failed to publish message, return code %d\n", rc);
                } else {
                    printf("Message published to %s\n", publish_topic);
                }
            } else {
                break;
            }
        }
    } else {
        printf("No publish topic specified - running in subscribe-only mode\n");
        while(1) {
            #if defined(_WIN32)
                Sleep(1000);
            #else
                sleep(1);
            #endif
        }
    }
    
    // Cleanup (unreachable in this example)
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    return rc;
}
